
#region Imported Namespaces
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;

using PHSRAG.Insight21.Admin;
using PHSRAG.Insight21.Context;
using PHSRAG.Insight21.SearchContext;
using Annunciator = PHSRAG.WebControls.Annunciator;
using DataAccess = PHSRAG.Utility.DataAccess;
using Settings = PHSRAG.Utility.Settings;
using Pager = PHSRAG.WebControls.Pager;
using Policy = PHSRAG.Insight21.Policy;
#endregion

namespace PHSRAG.Insight21.Admin.Controls
{
	/// <summary>
	/// The Picker class manages the activities of loading and displaying Record data.  The display
	/// takes the form of a DataGrid which allows the user to select a PI by clicking on "Selected" checkbox column in a row.
	/// Since there could be many rows, the DataGrid supports pagination through the Pager control.
	/// </summary>
	public partial class RecordsAccessPicker : System.Web.UI.UserControl
	{
		#region Constants
		private const string ClientScriptKey = "clientScript_AF5B85BF-B353-4f6f-8AD2-A9CD8097999C";
		private const string DataKey = "data_AF5B85BF-B353-4f6f-8AD2-A9CD8097999C";
		private const string JavascriptViewAllCallback = "ResetPickerState";
		private const string RecordRightsCacheKey = "recordRights_AF5B85BF-B353-4f6f-8AD2-A9CD8097999C";
		private const string RecordTypeTableNameColumn	= "Name";
		private const string RecordTypeTableIdColumn	= "Id";		
        private const string JavascriptWindowTitle = "SetWindowTitle";
        #endregion

		#region Instance Variables (private)
		private Policy.ICachePolicy	cachePolicy;
		private Policy.ICachePolicy	pageCachePolicy;
		#endregion

		#region Private Properties
		/// <summary>
		/// Get/Set the data used to initialize the selected data grid. 
		/// </summary>
		private DataView SelectedRecords
		{
			get
			{
				Hashtable h = (Hashtable) pageCachePolicy.GetCachedObject(Keys.IncomingDataKey); 
				DataView dataView = h[Keys.RecordRightsKey] as DataView;

				if (dataView == null)
				{
					UserContext userContext = (UserContext)cachePolicy.GetCachedObject(UserContext.CacheKey);

					dataView = new DataView(User.GetDirectAccessRights(
					IsUserPrincipalInvestigator(userContext) ? userContext.DomainUserId : 0,
					userContext.IsKeyGiver ? userContext.DomainUserId : 0).RecordList.Copy());

					dataView.RowStateFilter = DataViewRowState.Added | DataViewRowState.Unchanged;
					dataView.Sort = "RecordNumber ASC";
				}
				return dataView;
			}
			set
			{
				Hashtable h = (Hashtable) pageCachePolicy.GetCachedObject(Keys.IncomingDataKey); 
				h[Keys.RecordRightsKey] = value;
				pageCachePolicy.Cache(Keys.IncomingDataKey, h); 
			}
		}
		
		/// <summary>
		/// Get the records data results based on the current search criteria. 
		/// </summary>
		private Records Records
		{
			get
			{
				Records records =
					cachePolicy.GetCachedObject(Records.CacheKey) as Records;

				if (records == null)
				{
					records = new Records(cachePolicy);

					if ((fundNumber.Text.Trim().Length > 0 || folderNumber.Text.Trim().Length > 0) &&
						protocolNumber.Text.Trim().Length > 0)
					{
						throw new Exception(@"Neither 'Fund #' nor 'Folder #' can be used in combination with 'Protocol #' to search for records.");
					}
					UserContext userContext = (UserContext)cachePolicy.GetCachedObject(UserContext.CacheKey);
					records.Load (Utility.DataAccess.GetConnectionString()
						,Convert.ToInt32(userContext.DomainUserId)
						,fundNumber.Text.Trim()
						,folderNumber.Text.Trim()
						,protocolNumber.Text.Trim()
						,lastName.Text.Trim()
						,firstName.Text.Trim()
						,orgSelector.SelectedOrganizationID
						,Convert.ToInt32(recordType.SelectedValue)
						,(!IsUserPrincipalInvestigator(userContext) &&
							!userContext.IsKeyGiver &&
							(userContext.ModuleRoleBitmask & (int)Security.Enumerations.ModuleRoleBit.PrincipalInvestigator) != 0) 
							? userContext.DomainUserId : 0
						,userContext.IsKeyGiver ? userContext.DomainUserId : 0);

					records.Cache();
				}
				return records;
			}
		}

		private string PickerKeyForFilteredData
		{
			get { return string.Format("filtered_{0}", DataKey); }
		}

		/// <summary>
		/// Gets the relevant user object from the cache if there is one otherwise loads 
		/// a new one based on the cached personID and cache's it.
		/// </summary>
		private User User
		{
			get
			{
				User user = cachePolicy.GetCachedObject(User.CacheKey) as Admin.User;

				Hashtable h = (Hashtable) pageCachePolicy.GetCachedObject(Keys.IncomingDataKey); 
				int personId = (int)h[Keys.PersonIdKey];
				if (user == null || (user.PersonId != personId))
				{
					user = new User(cachePolicy);
					user.Load(Utility.DataAccess.GetConnectionString(), personId);
					user.Cache();
				}

				return user;
			}
		}

		#endregion

		#region Pagination Event Handlers
		/// <summary>
		/// Handler for the FirstPage event raised by the Pager. Force the data grid to display the first page.
		/// </summary>
		/// <param name="sender">Page control</param>
		/// <param name="e">Arguments associated with the event</param>
		/// <remarks>
		/// If the data grid is already displaying the first page, no further action is taken.
		/// </remarks>
		protected void OnFirstPage(object sender, System.Web.UI.WebControls.CommandEventArgs e)
		{
			recordDataGrid.CurrentPageIndex = 0;
			LoadRecordDataGrid();
		}

		/// <summary>
		/// Handler for the LastPage event raised by the Pager. Force the data grid to display the last page.
		/// </summary>
		/// <param name="sender">Page control</param>
		/// <param name="e">Arguments associated with the event</param>
		/// <remarks>
		/// If the data grid is already displaying the last page, no further action is taken.
		/// </remarks>
		protected void OnLastPage(object sender, System.Web.UI.WebControls.CommandEventArgs e)
		{
			recordDataGrid.CurrentPageIndex = pager.PageCount - 1;
			LoadRecordDataGrid();
		}

		/// <summary>
		/// Handler for the NextPage event raised by the Pager. Force the data grid to display the next page.
		/// </summary>
		/// <param name="sender">Page control</param>
		/// <param name="e">Arguments associated with the event</param>
		/// <remarks>
		/// If the data grid is already displaying the last page, no further action is taken.
		/// </remarks>
		protected void OnNextPage(object sender, System.Web.UI.WebControls.CommandEventArgs e)
		{
			recordDataGrid.CurrentPageIndex++;
			LoadRecordDataGrid();
		}

		/// <summary>
		/// Handler for the PreviousPage event raised by the Pager. Force the data grid to display the previous page.
		/// </summary>
		/// <param name="sender">Page control</param>
		/// <param name="e">Arguments associated with the event</param>
		/// <remarks>
		/// If the data grid is already displaying the first page, no further action is taken.
		/// </remarks>
		protected void OnPreviousPage(object sender, System.Web.UI.WebControls.CommandEventArgs e)
		{
			recordDataGrid.CurrentPageIndex--;
			LoadRecordDataGrid();
		}
		#endregion

		#region Event Handlers

		/// <summary>
		/// In the search results, if the record is already selected, disable the checkbox in the "Selected" column.
		/// </summary>
		/// <param name="sender">Control being loaded</param>
		/// <param name="e">Argument associated with the event</param>
		protected void OnItemDataBound(object sender, System.Web.UI.WebControls.DataGridItemEventArgs e)
		{
			if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
			{
				string resourceId = e.Item.Cells[0].Text;
				DataRow[] rows = 
					SelectedRecords.Table.Select( String.Format("ResourceId = {0}", resourceId));
				if (rows.Length > 0)
				{
					((CheckBox)(e.Item.Cells[4]).Controls[1]).Enabled = false;
				}
			}
		}

		/// <summary>
		/// On first load of the control, if the user is a PI, initialize the last name and 
		/// first name search controls and disable them.
		/// </summary>
		/// <param name="sender">Control being loaded</param>
		/// <param name="e">Argument associated with the event</param>
		protected void Page_Load(object sender, System.EventArgs e)
		{
			annunciator.Title = string.Empty;
			annunciator.Message = string.Empty;

			if (!IsPostBack)
			{
				UserContext userContext = (UserContext)cachePolicy.GetCachedObject(UserContext.CacheKey);
				if (!IsUserPrincipalInvestigator(userContext) &&
					!userContext.IsKeyGiver &&
					(userContext.ModuleRoleBitmask & (int)Security.Enumerations.ModuleRoleBit.PrincipalInvestigator) != 0)
				{
					//The user is a PI and not a sysadmin nor keygiver so they can only search on their own records.  
					//Here we will default the PI first and last name fields to themself and disable them to enforce
					//the rule.
					lastName.Text = userContext.LastName;
					firstName.Text = userContext.FirstName;
					lastName.Enabled = false;
					firstName.Enabled = false;
				}

				recordType.DataSource		=	Records.RecordTypeTable;
				recordType.DataTextField	=	RecordTypeTableNameColumn;
				recordType.DataValueField	=	RecordTypeTableIdColumn;
				recordType.DataBind();

				select.Visible = recordDataGrid.Visible = pager.Visible =  false;
				LoadSelectedRecordDataGrid();

				annunciator.Title = "Note:";
				StringBuilder message = new StringBuilder();
				message.Append(@"Select search criteria and click the 'Search' button to find matching records.  ");

				if (selectedRecordDataGrid.Items.Count > 0)
					message.Append(@"Choose selected records to be removed and click 'Remove' button.  ");

				message.Append(@"Click 'Save' button to save or simply close window to cancel.  ");
				annunciator.Message = message.ToString();
			}
		}

		/// <summary>
		/// Handler for the Search button. Helps in filtering the data displayed in the data grid.
		/// </summary>
		/// <param name="sender">The control which raised the event</param>
		/// <param name="e">Argument associated with the event</param>
		protected void OnSearch(object sender, System.EventArgs e)
		{
			try
			{
				Records.UnCache();
				LoadRecordDataGrid();
				ManagePagination();
				select.Visible = pager.Visible;

				if (pager.Visible)
				{
					StringBuilder message = new StringBuilder();
					message.Append(@"Choose records from search results to be added and click the 'Select' button.  ");

					if (selectedRecordDataGrid.Items.Count > 0)
						message.Append(@"Choose selected records to be removed and click 'Remove' button.  ");

					message.Append(@"Click 'Save' button to save or simply close window to cancel.  ");
					annunciator.Message = message.ToString();
				}
				else
				{
					annunciator.Title = "Data Load Warning:";
					StringBuilder message = new StringBuilder();
					message.Append(@"No record data found matching the search criteria.  Adjust criteria and click 'Search' button for matching records");

					if (selectedRecordDataGrid.Items.Count > 0)
						message.Append(@"Choose selected records to be removed and click 'Remove' button.  ");

					message.Append(@"Click 'Save' button to save or simply close window to cancel.  ");
				}
			}
			catch (Exception ex)
			{
				select.Visible = pager.Visible = recordDataGrid.Visible = false;
				annunciator.Message = ex.Message;
			}
		}

		/// <summary>
		/// Any item selected on the search results data grid will be added to the selected
		/// datagrid's data source and both grids are reloaded.
		/// </summary>
		/// <param name="sender">The control which raised the event</param>
		/// <param name="e">Argument associated with the event</param>
		protected void OnSelectRecords(object sender, System.EventArgs e)
		{
			try
			{
				DataView dv = SelectedRecords;
				foreach (DataGridItem item in recordDataGrid.Items)
				{
					if (item.ItemType == ListItemType.Item || item.ItemType == ListItemType.AlternatingItem || item.ItemType == ListItemType.SelectedItem)
					{
						if ( ((CheckBox)(item.Cells[4]).Controls[1]).Checked == true)
						{
							DataRow row = dv.Table.NewRow();
							row["ResourceId"] = Convert.ToInt32(item.Cells[0].Text);
							row["RecordNumber"] = item.Cells[1].Text;
							row["Description"] = item.Cells[2].Text;
							row["Investigator"] = item.Cells[3].Text;
							row["FwkDomainUserId"] = User.DomainUserId;
							row["AccessRightBitMask"] = 0;
							row["Comment"] = "";

							dv.Table.Rows.Add(row);
						}
					}
				}
	            
				SelectedRecords = dv;
				LoadRecordDataGrid();
				LoadSelectedRecordDataGrid();
			}
			catch (Exception ex)
			{
				annunciator.Message = ex.Message;
			}
		}

		/// <summary>
		/// Any item selected on the selected datagrid data grid will be removed from the selected
		/// datagrid's data source and both grids are reloaded.
		/// </summary>
		/// <param name="sender">The control which raised the event</param>
		/// <param name="e">Argument associated with the event</param>
		protected void OnRemoveRecords(object sender, System.EventArgs e)
		{
			try
			{
				DataView dv = SelectedRecords;
				foreach (DataGridItem item in selectedRecordDataGrid.Items)
				{
					if ((item.ItemType == ListItemType.Item || item.ItemType == ListItemType.AlternatingItem || item.ItemType == ListItemType.SelectedItem)
						&& ( ((CheckBox)(item.Cells[4]).Controls[1]).Checked == true))
						dv.Table.Select(String.Format("ResourceId = {0}", item.Cells[0].Text))[0].Delete();
				}
				SelectedRecords = dv;
				
				if (recordDataGrid.Items.Count > 0)
					LoadRecordDataGrid();
				
				LoadSelectedRecordDataGrid();
			}
			catch (Exception ex)
			{
				annunciator.Message = ex.Message;
			}
		}

		/// <summary>
		/// Copy the selected records table to the cached user's DirectAccess rights table and
		/// and persist the cached user's direct access rights.  The popup will closes
		/// and refresh it's parent window, reloading the user access rights page.
		/// </summary>
		/// <param name="sender">Control being loaded</param>
		/// <param name="e">Argument associated with the event</param>
		protected void OnSave(object sender, System.EventArgs e)
		{
			UserContext userContext = (UserContext)cachePolicy.GetCachedObject(UserContext.CacheKey);

			User.GetDirectAccessRights(
				IsUserPrincipalInvestigator(userContext) ? userContext.DomainUserId : 0,
				userContext.IsKeyGiver ? userContext.DomainUserId : 0).RecordList = SelectedRecords.Table;


			SqlConnection connection = new SqlConnection(DataAccess.ConnectionString);			
			SqlTransaction transaction = null;
			try
			{
				connection.Open();
				transaction = connection.BeginTransaction("DirectAccessRights");
				User.GetDirectAccessRights(
                    IsUserPrincipalInvestigator(userContext) ? userContext.DomainUserId : 0,
                    userContext.IsKeyGiver ? userContext.DomainUserId : 0).Save(transaction);
				User.UpdateUserResourceCache(transaction);
				transaction.Commit();
			}
			catch(Exception ex)
			{
				if(transaction != null)
					transaction.Rollback();

				annunciator.Message = ex.Message;
			}
			finally
			{
				if (connection.State != ConnectionState.Closed)
					connection.Close();
			}

			User.UnCache();
			Page.ClientScript.RegisterClientScriptBlock(GetType( ), ClientScriptKey, "<script language='javascript'>window.opener.document.location.href='../Admin/AdminUserAccessRights.aspx';window.close();</script>");
			cachePolicy.Cache(Keys.SaveEditStateKey, true); 
		}

		#endregion

        #region Overrides
        /// <summary>
        /// Register clientside scripts.
        /// </summary>
        /// <param name="e">Argument associated with the event.</param>
        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);
            if (!Page.ClientScript.IsClientScriptBlockRegistered(JavascriptWindowTitle))
            {
                string script =
                    "<script language=\"javascript\">\n" +
                    "\twindow.pageTitle.text = \"Records Access Search\";\n" +
                    "</script>\n";

                Page.ClientScript.RegisterClientScriptBlock(GetType( ), JavascriptWindowTitle, script);
            }
        }
        #endregion

		#region Private Methods

		/// <summary>
		/// Used to determine if the user is a System Administrator
		/// </summary>
		/// <param name="userContext">The user context information to be evaluated.</param>
		/// <returns>True if the user is a System Administrator otherwise False</returns>
		/// <param name="userContext"></param>
		/// <returns></returns>
		private bool IsUserPrincipalInvestigator(UserContext userContext)
		{
			return ((userContext.ModuleRoleBitmask & (int)Security.Enumerations.ModuleRoleBit.PrincipalInvestigator) != 0);
		}

		/// <summary>
		/// Load the DataGrid with record data. 
		/// Initialize the Pager based on the volume of data retrieved from the database. The navigation links on the
		/// Pager are also set.
		/// </summary>
		private void LoadRecordDataGrid()
		{
			Records records = Records;
			recordDataGrid.VirtualItemCount = pager.RecordCount = records.GetDataContainer().Tables[0].Rows.Count;

			int currentPageIndex = recordDataGrid.CurrentPageIndex;
			int pageCount = (pager.RecordCount / recordDataGrid.PageSize) +
				(int)(((pager.RecordCount % recordDataGrid.PageSize) == 0) ? 0 : 1);
			pager.PageCount = pageCount;
			pager.CurrentPage = currentPageIndex + 1;

			recordDataGrid.DataSource = Records.GetDataContainer().Tables[0];
			recordDataGrid.DataBind();

			ManagePagination();
		}

		/// <summary>
		/// Load the DataGrid with the cached user's access rights and any selections that have been made.
		private void LoadSelectedRecordDataGrid()
		{
			selectedRecordDataGrid.DataSource = SelectedRecords;
			selectedRecordDataGrid.DataBind();

			remove.Visible = (selectedRecordDataGrid.Items.Count > 0);
		}

		private void ManagePagination()
		{
			pager.Visible = recordDataGrid.Visible = (recordDataGrid.Items.Count > 0);
			if (recordDataGrid.Visible)
			{
				int currentPageIndex = this.recordDataGrid.CurrentPageIndex;
				recordDataGrid.CurrentPageIndex = currentPageIndex;
				pager.CurrentPage = currentPageIndex +1;

				int startIndex = currentPageIndex * recordDataGrid.PageSize;
				
				pager.FirstRecord = startIndex + 1;
				pager.LastRecord = Math.Min(startIndex + recordDataGrid.PageSize, recordDataGrid.VirtualItemCount);

				pager.EnableFirstPage = pager.EnablePreviousPage = (recordDataGrid.CurrentPageIndex != 0);

				pager.EnableLastPage = pager.EnableNextPage = (recordDataGrid.CurrentPageIndex != (pager.PageCount - 1));
			}
		}
		#endregion

		#region Web Form Designer generated code
		/// <summary>
		/// Initialize the control. Create dynamic columns in the data grid based on the pick type.
		/// </summary>
		/// <param name="e">Argument associated with the event</param>
		override protected void OnInit(EventArgs e)
		{
			//
			// CODEGEN: This call is required by the ASP.NET Web Form Designer.
			//
			InitializeComponent();
			base.OnInit(e);

			cachePolicy = (Policy.ICachePolicy)Page.Session[Policy.Names.CachePolicyKey];
			pageCachePolicy = (Policy.ICachePolicy)Page.Session[Policy.Names.CachePolicyKey];
		}
		
		/// <summary>
		///		Required method for Designer support - do not modify
		///		the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
		}
		#endregion
	}
}

// --- EOF ---
